package com.example.stock.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.db.Page;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.example.stock.comm.*;
import com.example.stock.entity.dao.AccountDao;
import com.example.stock.entity.eo.AccountEo;
import com.example.stock.entity.eo.ProBarStockEo;
import com.example.stock.entity.eo.StStockScoreEo;
import com.example.stock.entity.eo.StockTradeEo;
import com.example.stock.entity.mapper.ProBarStockMapper;
import com.example.stock.entity.mapper.StStockScoreMapper;
import com.example.stock.entity.service.*;
import com.example.stock.service.*;
import com.example.stock.vo.DataVo;
import com.example.stock.vo.ScoreVo;
import com.example.stock.vo.common.TradeVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/api/analysis")
@Slf4j
public class AnalysisController {

    @Resource
    private ProBarStockMapper proBarStockMapper;
    @Resource
    private StStockScoreMapper stStockScoreMapper;
    @Resource
    private IStockTradeService IStockTradeService;
    @Resource
    private IProBarStockService IProBarStockService;
    @Resource
    private IStStockScoreService IStStockScoreService;
    @Resource
    private IServiceCalc serviceCalc;
    @Resource
    private IServiceAnalysis serviceAnalysis;
    @Resource
    private IServiceTrade serviceProBar;
    @Resource
    private IServiceRowData serviceRowData;
    @Resource
    private IServiceAmountVol serviceAmountVol;

    @RequestMapping("statisticsBatch")
    public Object statisticsBatch(@RequestParam(required = false) List<String> tsCodes,
                                  @RequestParam(required = false) LocalDateTime startTime,
                                  @RequestParam(required = false) LocalDateTime endTime,
                                  @RequestParam(required = false) String asset,
                                  @RequestParam(required = false) String strategy,
                                  @RequestParam(required = false) String freq,
                                  @RequestParam(required = false) String tran
    ) {

        StrategyEnum strategyEnum = StrategyEnum.getByNameExt(strategy);
        FreqEnum freqEnum = FreqEnum.getByNameExt(freq);
        TranEnum tranEnum = TranEnum.getByNameExt(tran);
        AssetEnum assetEnum = AssetEnum.getByNameExt(asset);

        Map<String, List<DataVo>> closeDataMap;
        int more;
        switch (strategyEnum) {
            case EMA_UP:
            case MA_UP:  //1
            case EMA_REF0:
            case MA_CROSS:
                closeDataMap = serviceRowData.getRowDataWithFreqBatchGroupByCode(tsCodes, startTime, endTime, freqEnum, assetEnum, false, 100);
                for (int m = 10; m < 100; m += 1) {
                    for (int n = 10; n < 100; n += 1) {
                        serviceAnalysis.analysisBatch(closeDataMap, strategyEnum, m, n, tranEnum, startTime, 100);
                    }
                }
                break;
            case MA_PURE:
            case EMA_PURE:
                for (int m = 10; m < 100; m += 1) {
                    more = m;
                    closeDataMap = serviceRowData.getRowDataWithFreqBatchGroupByCode(tsCodes, startTime, endTime, freqEnum, assetEnum, false, more);
                    serviceAnalysis.analysisBatch(closeDataMap, strategyEnum, m, 0, tranEnum, startTime, more);
                }
                break;
            case EMA_UP_COMPLEX:
                for (int m = 1; m < 30; m += 1) {
                    for (int n = 10; n < 100; n += 1) {
                        more = m > n ? m : n;
                        closeDataMap = serviceRowData.getRowDataWithFreqBatchGroupByCode(tsCodes, startTime, endTime, freqEnum, assetEnum, false, more);
                        serviceAnalysis.analysisBatch(closeDataMap, strategyEnum, m, n, tranEnum, startTime, more);
                    }
                }
                break;
            default:
                throw new RuntimeException("枚举有误" + strategyEnum);

        }
        return "success!";
    }

    @RequestMapping("statisticsOne")
    public Object statisticsOne(@RequestParam(required = false) String tsCode,
                                @RequestParam(required = false) LocalDateTime startTime,
                                @RequestParam(required = false) LocalDateTime endTime,
                                @RequestParam(required = false) String asset,
                                @RequestParam(required = false) String strategy,
                                @RequestParam(required = false) String freq,
                                @RequestParam(required = false) String tran
    ) {

        StrategyEnum strategyEnum = StrategyEnum.getByNameExt(strategy);
        FreqEnum freqEnum = FreqEnum.getByNameExt(freq);
        TranEnum tranEnum = TranEnum.getByNameExt(tran);
        AssetEnum assetEnum = AssetEnum.getByNameExt(asset);

        List<DataVo> closeData;
        switch (strategyEnum) {
            case EMA_UP:
            case MA_UP:  //1
            case EMA_REF0:
            case MA_CROSS:
            case EMA_UP2:
            case EMA_UP_HEAD:
            case EMA_UP_SIMPLE:
            case EMA_UP_CLOSE:
                closeData = serviceRowData.getRowDataWithFreqOne(tsCode, startTime, endTime, freqEnum, assetEnum, false, 100);
                for (int m = 10; m < 100; m += 1) {
                    for (int n = 10; n < 100; n += 1) {
                        serviceAnalysis.analysisOne(closeData, tsCode, strategyEnum, m, n, tranEnum, startTime, 100);
                    }
                }
                break;
            case DEA_UP:
                closeData = serviceRowData.getRowDataWithFreqOne(tsCode, startTime, endTime, freqEnum, assetEnum, false, 200);
                for (int m = 1; m < 200; m += 1) {
                    for (int n = m + 1; n < 200; n += 1) {
                        serviceAnalysis.analysisOne(closeData, tsCode, strategyEnum, m, n, tranEnum, startTime, 200);
                    }
                }
                break;
            case MA_PURE:
            case EMA_PURE:
            case EMA_UP_SS:
                closeData = serviceRowData.getRowDataWithFreqOne(tsCode, startTime, endTime, freqEnum, assetEnum, false, 100);
                for (int m = 10; m < 100; m += 1) {
                    serviceAnalysis.analysisOne(closeData, tsCode, strategyEnum, m, 0, tranEnum, startTime, 100);
                }
                break;
            case EMA_UP_COMPLEX:
                closeData = serviceRowData.getRowDataWithFreqOne(tsCode, startTime, endTime, freqEnum, assetEnum, false, 100);
                for (int m = 1; m < 30; m += 1) {
                    for (int n = 10; n < 100; n += 1) {
                        serviceAnalysis.analysisOne(closeData, tsCode, strategyEnum, m, n, tranEnum, startTime, 100);
                    }
                }
                break;
            case EMA_UP_REF:
                closeData = serviceRowData.getRowDataWithFreqOne(tsCode, startTime, endTime, freqEnum, assetEnum, false, 100);
                for (int m = 1; m < 100; m += 1) {
                    System.out.println("------------------------------" + m);
//                    for (int n = 10; n < 100; n += 1) {
                    serviceAnalysis.analysisOne(closeData, tsCode, strategyEnum, m, 0, tranEnum, startTime, 100);
//                    }
                }
                break;
            default:
                throw new RuntimeException("枚举有误" + strategyEnum);

        }
        return "success!";
    }


    @RequestMapping("statistics_stock")
    public void statistics_stock(@RequestParam(required = false) LocalDateTime time) {
        List<String> tsCodes = serviceAmountVol.listTopTen(time);
        System.out.println(tsCodes);
        statisticsBatch(tsCodes, time, time.plusMonths(6),
                AssetEnum.STOCK.name(),
                StrategyEnum.EMA_UP.name(),
                FreqEnum.MIN30.name(),
                TranEnum.T1.name());
    }

    @RequestMapping("compose")
    public Object compose(@RequestParam String compose,
                          @RequestParam String freq,
                          @RequestParam(defaultValue = "1000.0") Double firstIndex,
                          @RequestParam LocalDateTime startTime,
                          @RequestParam LocalDateTime endTime
    ) {
        return serviceProBar.compose(compose, freq, firstIndex, startTime, endTime);
    }

    @RequestMapping("start")
    public Object start() {
        LocalDateTime startTime = Cons.year2018;
        LocalDateTime endTime = startTime.plusMonths(3);
        List<String> tsCodes = refreshStockPool(startTime);
        int skip = 100;
        Map<String, List<DataVo>> rowData30Batch = IProBarStockService.getRowData30Batch(startTime, endTime, tsCodes, skip);
        Map<String, List<TradeVo>> stockTradeVo = IProBarStockService.calcEmaUpTrade(rowData30Batch);
        Map<String, List<StockTradeEo>> codeMapTradeVo = IProBarStockService.doTrade(stockTradeVo);
        List<StockTradeEo> stockTradeEos = IProBarStockService.combineTrade(codeMapTradeVo);

        return null;

    }

    private List<String> refreshStockPool(LocalDateTime startTime) {
        return new ArrayList<>();
    }

    @RequestMapping("analysis_30")
    public Object analysis_30______() {
        System.out.println("======================================");
        System.out.println("======================================");
        System.out.println("======================================");
        System.out.println("======================================");
        System.out.println("======================================");
        System.out.println("======================================");
        System.out.println("======================================");
        List<String> tsCodeList = proBarStockMapper.listTsCode();
        int i = 1;
        for (String tsCode : tsCodeList) {
            List<ProBarStockEo> proBarStockEos = proBarStockMapper.selectList_(tsCode);
            proBarStockEos = proBarStockEos.stream()
//                    .filter(t->t.getTradeTime().isBefore(Cons.year2020))
                    .filter(t -> t.getTradeTime().isAfter(Cons.year2020))
                    .collect(Collectors.toList());

            if (proBarStockEos.size() == 0) {
                continue;
            }
            double amountAvg = proBarStockEos.stream().mapToDouble(ProBarStockEo::getAmount).sum() / proBarStockEos.size() * 8;
            if (amountAvg < 200000000.0) {
                proBarStockMapper.delete(Wrappers.update(new ProBarStockEo().setTsCode(tsCode)));
                System.out.println(tsCode + "日均成交量少于30000000.0:" + amountAvg);
            }
        }
        return tsCodeList;
    }

    public Object statistics_30(boolean test) {
        List<String> tsCodeList = proBarStockMapper.listTsCode();
        System.out.println(tsCodeList.size());
        List<String> finishCodes = stStockScoreMapper.getTsCode();
        tsCodeList.removeAll(finishCodes);
        if (test) {
            System.out.println(tsCodeList.size());
            System.out.println(finishCodes.size());
            return tsCodeList;
        }
        int size = tsCodeList.size();
        int finish = 0;
        for (String tsCode : tsCodeList) {
            List<StStockScoreEo> inserts = new ArrayList<>();
            List<ProBarStockEo> proBarStockEos = proBarStockMapper.selectList(Wrappers.lambdaQuery(new ProBarStockEo().setTsCode(tsCode)));
            for (int i = 5; i < 50; i++) {
                for (int j = 5; j < 50; j++) {
                    StStockScoreEo stStockScoreEo = analysis_60(j * 2, i * 2, tsCode, proBarStockEos);
                    inserts.add(stStockScoreEo);

                }
            }
            IStStockScoreService.saveBatch(inserts);
            System.out.println(++finish * 1.0 / size * 100 + "%");
        }

        return null;
    }

    public StStockScoreEo analysis_60(int m, int n, String tsCode, List<ProBarStockEo> proBarStockEos) {
//        List<String> tsCodeList =  proBarStockMapper.listTsCode();
//        int i=1;
//        for (String tsCode : tsCodeList) {
//            List<ProBarStockEo> proBarStockEos = proBarStockMapper.selectList(Wrappers.lambdaQuery(new ProBarStockEo().setTsCode(tsCode)));

        List<DataVo> closeData = proBarStockEos.stream()
                .filter(t -> Cons.time60Min.contains(t.getTradeTime().format(Cons.formatHHmm)))
                .map(t -> new DataVo()
                        .setPrice(t.getClose())
                        .setTradeTime(t.getTradeTime())
                )
                .sorted(Comparator.comparing(DataVo::getTradeTime))
                .collect(Collectors.toList());

        List<TradeVo> tradeVos = serviceCalc.calcEmaUpTrade(closeData, m, n);
        tradeVos = tradeVos.stream().skip(40).collect(Collectors.toList());


        List<StockTradeEo> stockTradeEos = IProBarStockService.calcTrade(tradeVos, tsCode);

        ScoreVo score = IStockTradeService.getScore(stockTradeEos);
        score.setTsCode(tsCode);
        StStockScoreEo entity = BeanUtil.copyProperties(score, StStockScoreEo.class);
        entity.setM(m);
        entity.setN(n);
        return entity;
//        }
//        return tsCodeList;
    }


    List<StStockScoreEo> stStockScoreEos = new ArrayList<>();

    public void view() {
        for (int j = 5; j < 50; j++) {
            final int m = j * 2;
            List<StStockScoreEo> list = IStStockScoreService.list(Wrappers.<StStockScoreEo>lambdaQuery().eq(StStockScoreEo::getM, m));
            Map<Integer, List<StStockScoreEo>> collect = list.stream().collect(Collectors.groupingBy(StStockScoreEo::getN));
            collect.forEach((k, v) -> {
                double asDouble = v.stream().mapToDouble(StStockScoreEo::getReturnRateYearRelative).average().getAsDouble();
                System.out.println("avg m , n" + asDouble + "--> " + m + "--> " + k);
                StStockScoreEo result = new StStockScoreEo().setScore(asDouble).setM(m).setN(k);
                stStockScoreEos.add(result);
            });

            stStockScoreEos = stStockScoreEos.stream().sorted((v1, v2) -> -(v1.getScore().compareTo(v2.getScore()))).limit(100).collect(Collectors.toList());

        }
        System.out.println(JSON.toJSONString(stStockScoreEos));
    }


    public List<StStockScoreEo> selectWellData(int m, int n) {
        return null;
    }


    public List<StStockScoreEo> filterViewDetail(int m, int n) {

        List<StStockScoreEo> list = IStStockScoreService.list(Wrappers.<StStockScoreEo>lambdaQuery()
                .eq(StStockScoreEo::getM, m)
                .eq(StStockScoreEo::getN, n)
        );
        int i = list.size() - 50;
        List<String> collect = list.stream()
                .sorted(Comparator.comparing(StStockScoreEo::getReturnRateYearRelative))
                .skip(i)
                .map(StStockScoreEo::getTsCode)
                .collect(Collectors.toList());
        boolean remove = IStStockScoreService.remove(Wrappers.<StStockScoreEo>lambdaUpdate().in(StStockScoreEo::getTsCode, collect));
        System.out.println(remove);
        System.out.println(collect);
        return list.stream().filter(t -> collect.contains(t.getTsCode())).collect(Collectors.toList());

    }

    public Object dataFilterMaxMin() {
        List<String> tsCodeList = proBarStockMapper.listTsCode();
        List<String> returnList = new ArrayList<>();
        for (String tsCode : tsCodeList) {
            List<ProBarStockEo> proBarStockEos = proBarStockMapper.selectList_(tsCode);
            proBarStockEos.forEach(t -> t.setTradeTime(t.getTradeTime().withDayOfMonth(1).withHour(1).withMinute(1).withSecond(1)));
            Map<LocalDateTime, List<ProBarStockEo>> collect = proBarStockEos.stream().collect(Collectors.groupingBy(ProBarStockEo::getTradeTime));
            Collection<List<ProBarStockEo>> values = collect.values();
            List<List<ProBarStockEo>> collect1 = values.stream().filter(t -> judge(t)).collect(Collectors.toList());

            double v = collect1.size() * 1.0 / values.size();
            if (v >= 0.0001) {
                returnList.add(tsCode);
                System.out.println(tsCode + "频率: " + v);
                System.out.println(collect1.get(0).get(0).getTradeTime());
//                proBarStockMapper.delete(Wrappers.update(new ProBarStockEo().setTsCode(tsCode)));
            }
        }
        return returnList;
    }

    private boolean judge(List<ProBarStockEo> t) {
        if (CollectionUtil.isEmpty(t)) {
            return false;
        }
        double max = t.stream().mapToDouble(ProBarStockEo::getClose).max().getAsDouble();
        double min = t.stream().mapToDouble(ProBarStockEo::getClose).min().getAsDouble();
        if (max == min) {
            return false;
        }
        return (max - min) / (max + min) * 2 < 0.04;

    }


    public void statisticsUnit(@RequestParam(required = false) List<String> tsCodes,
                               @RequestParam(required = false) LocalDateTime startTime,
                               @RequestParam(required = false) LocalDateTime endTime,
                               @RequestParam(required = false) String asset,
                               @RequestParam(required = false) String freq,
                               @RequestParam(required = false) String tran
    ) {

        FreqEnum freqEnum = FreqEnum.getByNameExt(freq);
        TranEnum tranEnum = TranEnum.getByNameExt(tran);
        AssetEnum assetEnum = AssetEnum.getByNameExt(asset);

        Map<String, List<DataVo>> closeDataMap = serviceRowData.getRowDataWithFreqBatchGroupByCode(tsCodes, startTime, endTime, freqEnum, assetEnum, false, 100);

//        Double aDouble1 = serviceAnalysis.analysis00(closeDataMap, StrategyEnum.EMA_UP, tranEnum, startTime, 100);
        for (int m = 20; m < 100; m += 1) {
            for (int n = 20; n < 100; n += 1) {
//                if (m != 38 || n != 46) {
////                    AccountEo accountEo = new AccountEo();
////                    accountEo.setParam1(m);
////                    accountEo.setParam2(n);
////                    accountEo.setFinalMoney(aDouble1);
////                    saveAccount(accountEo);
//                }
//                else {
                Double aDouble = serviceAnalysis.analysisUnit(closeDataMap, StrategyEnum.EMA_UP, m, n, tranEnum, startTime, 100);
                AccountEo accountEo = new AccountEo();
                accountEo.setParam1(m);
                accountEo.setParam2(n);
                accountEo.setFinalMoney(aDouble);

                saveAccount(accountEo);

//                }

            }
        }
        checkAccount();

    }


    public void statisticsUnitA(@RequestParam(required = false) List<String> tsCodes,
                                @RequestParam(required = false) LocalDateTime startTime,
                                @RequestParam(required = false) LocalDateTime endTime,
                                @RequestParam(required = false) String asset,
                                @RequestParam(required = false) String freq,
                                @RequestParam(required = false) String tran
    ) {

        FreqEnum freqEnum = FreqEnum.getByNameExt(freq);
        TranEnum tranEnum = TranEnum.getByNameExt(tran);
        AssetEnum assetEnum = AssetEnum.getByNameExt(asset);

        Map<String, List<DataVo>> closeDataMap = serviceRowData.getRowDataWithFreqBatchGroupByCode(tsCodes, startTime, endTime, freqEnum, assetEnum, false, 100);

//        Double aDouble1 = serviceAnalysis.analysis00(closeDataMap, StrategyEnum.EMA_UP, tranEnum, startTime, 100);
        for (int i = 0; i < 100; i++) {
            Double aDouble = serviceAnalysis.analysisUnitA(closeDataMap, StrategyEnum.EMA_UP, 38, 46, i, tranEnum, startTime, 100);
            AccountEo accountEo = new AccountEo();
            accountEo.setParam1(i);
            accountEo.setParam2(0);
            accountEo.setFinalMoney(aDouble);
            saveAccount(accountEo);
        }

        checkAccount();

    }


    private List<AccountEo> accountEos = new ArrayList<>();
    private int listSize = 0;
    @Resource
    private AccountDao accountDao;

    private void saveAccount(AccountEo accountEo) {
        accountEos.add(accountEo);
        if (accountEos.size() > 200) {
            accountDao.saveBatch(accountEos);
            listSize += accountEos.size();
            accountEos.clear();
        }
    }

    private void checkAccount() {
        if (CollectionUtil.isNotEmpty(accountEos)) {
            accountDao.saveBatch(accountEos);
            listSize += accountEos.size();
            accountEos.clear();
        }
        log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% size : " + listSize);
    }
}
